using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace StupidGrid
{
    [Description("StupidGrid Control")]
    [ValidationProperty("IsValid")]
    [ToolboxData("<{0}:StupidGrid runat=server></{0}:StupidGrid>")]
    public sealed class StupidGrid : Control, INamingContainer
    {
        public string UserControlPath { get; set; }
        public bool WrapFooterInTableRow { get; set; }
        public string AddButtonText { get; set; }
        public event EventHandler StupidClick;

        private PlaceHolder MainPlaceholder { get; set; }
        private Button Add { get; set; }
        private PlaceHolder FooterPlaceholder { get; set; }

        private const string ControlRowPrefix = "StupidGridControlID_";

        public StupidGrid()
        {
            MainPlaceholder = new PlaceHolder();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if(string.IsNullOrEmpty(AddButtonText))
            {
                AddButtonText = "Add";
            }

            Add = new Button { Text = AddButtonText };
            Add.Click += Add_Click;

            Controls.Add(MainPlaceholder);

            FooterControlTreeSetup();

            if (MyPage.IsPostBack)
            {
                RecreateControls();
            }
        }

        public void SetUserControls(IEnumerable<UserControl> userControls)
        {
            var rowsToRemove = MainPlaceholder.Controls.OfType<UserControl>();
            rowsToRemove.ToList().ForEach(
                usercontrol =>
                        MainPlaceholder.Controls.Remove(usercontrol)
                );

            userControls.ToList().ForEach(
                userControl =>
                {
                    userControl.ID = BuildID(NumberOfControls);
                    MainPlaceholder.Controls.Add(userControl);
                    NumberOfControls++;
                }
                );
        }

        public IEnumerable<T> RetrieveUserControls<T>()
        {
            return MainPlaceholder.Controls.OfType<T>();
        }        

        // this is the hackiest part
        // if we rebuild the controls with the same id's, then the data entered into inputs will persist
        private void RecreateControls()
        {
            for (int i = 0; i < NumberOfControls; i++)
            {
                if (!DeletedList.Contains(i))
                {
                    var uc = NewUserControl(i);
                    ReplaceStupidDeleteButtons(uc, i);
                    MainPlaceholder.Controls.Add(uc);
                }
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            NumberOfControls = MainPlaceholder.Controls.OfType<UserControl>().Count() + DeletedList.Count;
        }

        protected override void Render(HtmlTextWriter writer)
        {
            VerifyArguments();

            base.Render(writer);
        }

        // climb up the control tree until you get to the parent page
        private Page MyPage
        {
            get
            {
                var parent = Parent;
                while(parent != null && ((parent as Page) == null))
                {
                    parent = parent.Parent;
                }
                return parent as Page;
            }
        }

        private int NumberOfControls
        {
            get
            {
                if (ViewState["StupidGridNumberOfControls"] == null)
                {
                    ViewState["StupidGridNumberOfControls"] = 0;
                }
                return (int) ViewState["StupidGridNumberOfControls"];
            }
            set
            {
                ViewState["StupidGridNumberOfControls"] = value;
            }
        }

        private IList<int> DeletedList
        {
            get
            {
                if (ViewState["StupidGridDeletedList"] == null)
                {
                    ViewState["StupidGridDeletedList"] = new List<int>();
                }
                return (IList<int>)ViewState["StupidGridDeletedList"];
            }
            set
            {
                ViewState["StupidGridDeletedList"] = value;
            }
        }

        private void ReplaceStupidDeleteButtons(Control uc, int id)
        {
            var deleteButtonToReplace = uc.Controls.OfType<Button>()
                .SingleOrDefault(button => button.ID == "stupidgrid_delete");
            if (deleteButtonToReplace != null)
            {
                var stupidDeleteButton = new Button();
                // put in default text if the outgoing button doesn't have any
                stupidDeleteButton.Text = string.IsNullOrEmpty(deleteButtonToReplace.Text)
                                              ? "Del"
                                              : deleteButtonToReplace.Text;
                stupidDeleteButton.CommandArgument = id.ToString();
                stupidDeleteButton.Click += delbutton_Click;

                var index = uc.Controls.IndexOf(deleteButtonToReplace);
                uc.Controls.AddAt(index, stupidDeleteButton);
                uc.Controls.Remove(deleteButtonToReplace);
            }
        }

        private void delbutton_Click(object sender, EventArgs e)
        {
            var id = int.Parse(((Button) sender).CommandArgument);
            DeletedList.Add(id);
            var rowToDelete = MainPlaceholder.Controls.OfType<UserControl>()
                .Single(control => control.ID == BuildID(id));
            MainPlaceholder.Controls.Remove(rowToDelete);
            if (StupidClick != null) StupidClick(this, e);
        }

        private void FooterControlTreeSetup()
        {
            if (WrapFooterInTableRow)
            {
                var footerCell = new TableCell();
                footerCell.Controls.Add(Add);
                var footerRow = new TableRow();
                footerRow.Controls.Add(footerCell);
                FooterPlaceholder = new PlaceHolder();
                FooterPlaceholder.Controls.Add(footerRow);
                Controls.Add(FooterPlaceholder);
            }
            else
            {
                Controls.Add(Add);
            }
        }

        private void Add_Click(object sender, EventArgs e)
        {
            var newUserControl = NewUserControl(NumberOfControls);
            ReplaceStupidDeleteButtons(newUserControl,NumberOfControls);
            MainPlaceholder.Controls.Add(newUserControl);
            if (StupidClick != null) StupidClick(this, e);
        }

        private UserControl NewUserControl(int idnum)
        {
            var uc = (UserControl)MyPage.LoadControl(UserControlPath);
            uc.ID = BuildID(idnum);
            return uc;
        }

        private static string BuildID(int idnum)
        {
            return ControlRowPrefix + idnum;
        }

        private void VerifyArguments()
        {
            if (string.IsNullOrEmpty(UserControlPath))
            {
                throw new ArgumentException("UserControlPath must be set on StupidGrid (" + this.ID + ")");
            }

            if (MyPage == null)
            {
                throw new InvalidOperationException("StupidGrid (" + this.ID +
                                                    ") must be placed on a page (no Page parent found)");
            }

            UserControl uc;
            try
            {
                uc = MyPage.LoadControl(UserControlPath) as UserControl;
            }
            catch (Exception)
            {
                throw new FileNotFoundException("StupidGrid could not find a UserControl at: " + UserControlPath);
            }

            if (uc == null)
            {
                throw new ArgumentException("The control was not a UserControl: " + UserControlPath);
            }
            uc.Dispose();
        }

        public string IsValid
        {
            get
            {
                var controlsToValidate = MainPlaceholder.Controls.OfType<UserControl>();
                if (controlsToValidate.Count() == 0)
                {
                    return "";
                }
                foreach (var row in controlsToValidate)
                {
                    var validatorsToCheck = row.Controls.OfType<BaseValidator>();
                    foreach (var valid in validatorsToCheck)
                    {
                        valid.Validate();
                        if (!valid.IsValid)
                        {
                            return "";
                        }
                    }
                }
                return "Valid";
            }
        }
    }
}
